/**********************************
 * Assignment 5: Alien vs Predator
 * Date: 12/04/2011
 *
 * Alexander Miller: ahm82, 2605797
 * David Felty: djf242, 2828357
 * David Hau: dch229, 2586217
 * Hanna Atmer: ha237, 2666426
 **********************************/

package avp;

import java.util.EnumMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import ugraph.UGraphComponent;

// Represents a generic Node in a graph
public class Node implements UGraphComponent<Edge> {
	// The set of all edges that exist in the graph extending from this node
	Set<Edge> edges = new HashSet<Edge>();
	// Maps compass directions to edges of this node
	// Edges in this map don't exist in the graph containing this node if the edge is a wall
	private Map<Dir,Edge> directionalEdges = new EnumMap<Dir,Edge>(Dir.class);
	// The row of this node in the grid
	final int row;
	// The column of this node in the grid
	final int col;
	// Used for Ship.spanningTree()
	int rootVal;
	// Used to create distinct "floating nodes," which don't exist in the coordinate
	// system of a ship
	private static int floatingNodeID = -1;
	
	// Creates a floating node that doesn't fit into a ship's grid. It's not useful for this
	// project, but it ensures that nodes created with this method will not return true for
	// the .equals(...) method, so they can be put in the same set without overwriting each other
	public Node() {
		this(floatingNodeID, floatingNodeID);
		floatingNodeID--;
	}
	
	public Node(int row, int col) {
		this.row = row;
		this.col = col;
	}

	public Set<Edge> getAdjacent() {
		return edges;
	}

	// Returns the set of all directional edges (including walls) extending from this node
	public Set<Edge> allEdges() {
		return new HashSet<Edge>(directionalEdges.values());
	}

	// Set the edge extending in direction d from this node
	public void set(Dir d, Edge e) {
		Edge old = directionalEdges.get(d);
		edges.remove(old);
		if (e.state != null) edges.add(e);
		directionalEdges.put(d, e);
	}
	
	// Get the edge extending in direction d from this node
	public Edge get(Dir d) {
		return directionalEdges.get(d);
	}
	
	// Gets the edge between this node and another node
	public Edge edgeTo(Node other) {
		for (Edge e1 : directionalEdges.values()) {
			for (Edge e2 : other.directionalEdges.values()) {
				if (e1 == e2) return e1;
			}
		}
		throw new IllegalArgumentException(this + " not adjacent to " + other);
	}
	
	// Gets the node that is connected to this node through the specified edge
	public Node getOtherThru(Edge e) {
		assert edges.contains(e);
		for (Node n : e.nodes) {
			if (n != this) return n;
		}
		// For our purposes, a node shouldn't be connected to itself
		throw new IllegalStateException();
	}
	
	// Returns all surrounding edges, including walls
	public Node[] getAdjacentNodes() {
		Node[] ns = new Node[directionalEdges.size()];
		Iterator<Edge> it = directionalEdges.values().iterator();
		
		for (int i = 0; i < ns.length; i++) {
			ns[i] = getOtherThru(it.next());
		}
		return ns;
	}
	
	public boolean equals(Object other) {
		if (!(other instanceof Node)) return false;
		Node n = (Node) other;
		return (row == n.row) && (col == n.col);
	}
	
	public int hashCode() {
		// hash code algorithm taken from java.awt.Dimension.hashCode
		int sum = row + col;
		return sum * (sum + 1) / 2 + row;
	}
	
	public String toString() {
		return String.format("Node %d %d", row, col);
	}
	
	public Node clone() {
		Node n = new Node(this.row, this.col);
		for (Edge e : this.edges) {
			n.edges.add(e.clone());
		}
		n.directionalEdges.putAll(this.directionalEdges);
		n.rootVal = this.rootVal;
		
		return n;
	}
}
